/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.painting.resources;

import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.hercules.android.receiver.LogUtil;
import com.hercules.android.receiver.handlers.events.EventRaisingException;
import com.hercules.android.receiver.model.commands.XRECommand;
import com.hercules.android.receiver.model.commands.XRESetCommand;
import com.hercules.android.receiver.model.events.XREHTMLEvent;
import com.hercules.android.receiver.model.objects.XREHTMLText;
import com.hercules.android.receiver.model.objects.XREResourceOptions;
import com.hercules.android.receiver.painting.PaintersUtil;
import com.hercules.android.receiver.painting.views.AndroidXREView;
import com.hercules.android.receiver.painting.views.XRELayoutParams;

/**
 * Painter for {@link XREHTMLText} resource. It will use {@link WebView} to display html text using css styles.
 * <p>
 * AndroidAssembly1 this class was added in Android Assembly 1
 * <p>
 * AndroidAssembly3 fixed some bugs
 * <p>
 * <b>Thread safety:</b> Not thread safe, because base class is not thread safe
 *
 * @author 7realm
 * @version 1.0
 */
public class XREHTMLTextPainter extends BaseXREResourcePainter {
    /** Represents prefix for onHTMLClicked event links. */
    private static final String HTML_URL_PREFIX = "event:";
    /** Represents webView that will be used to display HTML content. */
    private WebView webView;
    /** Represents default font size for WebView. */
    private int defaultFontSize;

    /**
     * Constructor for class {@link XREHTMLTextPainter}.
     *
     * @param xreResource the {@link XREHTMLText}
     */
    public XREHTMLTextPainter(XREHTMLText xreResource) {
        super(xreResource);
    }

    /**
     * Performs initialize actions.
     *
     * @throws IllegalStateException if required fields are not set
     */
    @Override
    public void init() {
        final String methodName = "init";
        LogUtil.enter(getLogger(), this, methodName);

        try {
            PaintersUtil.checkState(getReceiver(), "receiver");
            PaintersUtil.checkState(getXREView(), "xreView");
            PaintersUtil.checkState(getAndroidView(), "androidView");

            // add WebView to UI view
            synchronized (XREHTMLTextPainter.this) {
                // setup the web view
                webView = new WebView(getAndroidView().getContext());

                // get font size
                defaultFontSize = webView.getSettings().getDefaultFontSize();
            }

            webView.setBackgroundColor(0);
            webView.setClipToPadding(false);
            webView.setClipChildren(false);
            webView.setHorizontalScrollBarEnabled(false);
            webView.setVerticalScrollBarEnabled(false);
            webView.setLayoutParams(new XRELayoutParams(getXREView()));

            // AndroidAssembly3 enable javascript
            webView.getSettings().setJavaScriptEnabled(true);

            // add html link click listener
            webView.setWebViewClient(new WebViewClient() {
                /**
                 * Invalidates parent view after loading.
                 *
                 * @param view the current view
                 * @param url the loaded url
                 */
                @Override
                public void onPageFinished(WebView view, String url) {
                    super.onPageFinished(view, url);

                    // invalidate parent
                    getAndroidView().invalidate();
                }

                /**
                 * Raises onHTMLLinkClicked event when link is clicked.
                 *
                 * @param view the current view
                 * @param url the url of clicked link
                 * @return true, because we handle url loading
                 */
                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                    if (url.startsWith(HTML_URL_PREFIX)) {
                        // prepare event
                        XREHTMLEvent event = new XREHTMLEvent();
                        event.setName(XREHTMLEvent.ON_HTML_LINK_CLICKED_EVENT_NAME);
                        event.setSource(getHTMLText().getId());
                        event.setValue(url.substring(HTML_URL_PREFIX.length()));

                        try {
                            // raise event
                            getEventRaiser().raiseEvent(event, getReceiver(), true);
                        } catch (EventRaisingException e) {
                            LogUtil.error(getLogger(), XREHTMLTextPainter.this, methodName, e);
                        }
                    } else {
                        // incorrect format of <a> html tag
                        LogUtil.warn(getLogger(), XREHTMLTextPainter.this, methodName,
                            "Incorrect format of url {0}, expected {1}<value>.", url, HTML_URL_PREFIX);
                    }
                    return true;
                }
            });

            // add view
            getAndroidView().addView(webView, 0);
            getAndroidView().setClipChildren(true);

            // load data and invalidate
            loadData(methodName);
            getAndroidView().invalidate();


        } catch (IllegalStateException e) {
            throw LogUtil.error(getLogger(), this, methodName, e);
        }

        LogUtil.exit(getLogger(), this, methodName);
    }

    /**
     * Performs clean up.
     *
     * @throws IllegalStateException if required fields are not set
     */
    @Override
    public void finish() {
        String methodName = "finish";
        LogUtil.enter(getLogger(), this, methodName);

        try {
            PaintersUtil.checkState(getReceiver(), "receiver");
            synchronized (this) {
                PaintersUtil.checkState(webView, "webView");
            }
            
            // remove view
            getAndroidView().removeView(webView);

            // release the web view
            webView.destroy();

        } catch (IllegalStateException e) {
            throw LogUtil.error(getLogger(), this, methodName, e);
        }

        LogUtil.exit(getLogger(), this, methodName);
    }

    /**
     * Handle {@link XRECommand}.
     *
     * @param command the command to handle
     */
    @Override
    public void handleCommand(XRECommand command) {
        final String methodName = "handleCommand";
        LogUtil.enter(getLogger(), this, methodName);

        if (webView != null) {
            if (command instanceof XRESetCommand) {
                XRESetCommand setCommand = (XRESetCommand) command;

                // apply layout if dimensions have changed
                if (setCommand.getProps().containsKey("dimensions")) {
                    webView.setLayoutParams(new XRELayoutParams(getXREView()));
                }
                
                // reload data and invalidate view
                // reload WebView
                loadData(methodName);

                webView.invalidate();
            }
        }
            
        LogUtil.exit(getLogger(), this, methodName);
    }

    /**
     * Loads data to HTML view.
     *
     * @param methodName the name of calling method
     */
    private void loadData(final String methodName) {
        XREHTMLText htmlText = getHTMLText();

        LogUtil.info(getLogger(), this, methodName, "Start loading HTML text: {0}", htmlText.getHtmlText());

        String css = "";
        if (htmlText.getStylesheet() != null) {
            css = htmlText.getStylesheet().getCss();
            // change standard fonts
            css = css.replace("font-family: with-serif;", "font-family: serif;");
            css = css.replace("font-family: without-serif;", "font-family: sans-serif;");
            css = css.replace("font-family: monospaced;", "font-family: monospace;");
        }
        
        XREResourceOptions options = getXREView().getResourceOptions();
        
        StringBuilder html = new StringBuilder();
        html.append(
        "<html><head>" +
        "<script type=\"text/javascript\">" +
        "function trim(str){" +
        "	return str.replace(/^\\s\\s*/, \'\').replace(/\\s\\s*$/, \'\');" +
    	"}" +
        "function applyCSSTexts(htmlTextElement, tagCSSTexts, classCSSTexts) {" +
		"	if (htmlTextElement.tagName && tagCSSTexts[htmlTextElement.tagName.toUpperCase()]) {" +
		"		htmlTextElement.style.cssText += tagCSSTexts[htmlTextElement.tagName.toUpperCase()];" +
		"	}" +	
		"	if (htmlTextElement.className && classCSSTexts[htmlTextElement.className.toUpperCase()]) {" +
		"		htmlTextElement.style.cssText += classCSSTexts[htmlTextElement.className.toUpperCase()];" +
		"	}" +
		"	var childNodes = htmlTextElement.childNodes;" +
		"	for (var i = 0; i < childNodes.length; ++i) {" +
		"		applyCSSTexts(childNodes[i], tagCSSTexts, classCSSTexts);" +
		"	}" +
		"}" +
		"function applyLineSpacing(htmlTextElement, lineSpacing) {" +
		"	if (htmlTextElement.style) {" +
		"		htmlTextElement.style.lineHeight = parseInt(htmlTextElement.style.fontSize) + lineSpacing + \"px\";" +
		"	}" +
		"	var childNodes = htmlTextElement.childNodes;" +
		"	for (var i = 0; i < childNodes.length; ++i) {" +
		"		applyLineSpacing(childNodes[i], lineSpacing);" +
		"	}" +
		"}" +
		"function removeUnderLineForLinks(htmlElement) {" +
		"	if (htmlElement.tagName == \"A\") {" +
		"		htmlElement.style.textDecoration = \"none\";" +
		"	}" +
		"	var childNodes = htmlElement.childNodes;" +
		"	for (var i = 0; i < childNodes.length; ++i) {" +
		"		removeUnderLineForLinks(childNodes[i]);" +
		"	}" +
		"}" +
		"function parseCSS(htmlElement, cssContent) {" +
		"	var sel = cssContent.split(\"}\");" +
		"	var tagCSSTexts = {}, classCSSTexts = {};" +
		"	for (var i = 0; i < sel.length; i++) {" +
		"   	sel[i] = trim(sel[i]);" +
		"    	if (sel[i].length == 0) {" +
		"        	continue;" +
		"    	}" +
		"		var startPos = sel[i].indexOf(\"{\");" +
		"		var theName = trim(sel[i].substring(0, startPos));" +
		"		var names = theName.split(/\\s+/);" +
		"		var cssText = trim(sel[i].substring(startPos + 1));" +
		"		for (var k = 0; k < names.length; k++) {" +
		"			var name = names[k];" +
		"			var pos = name.indexOf('.');" +
		"			if (pos >= 0) {" +
		"				var tagName = name.substring(0, pos).toUpperCase();" +
		"				var className = name.substring(pos + 1).toUpperCase();" +
		"				if (!tagCSSTexts[tagName]) {" +
		"					tagCSSTexts[tagName] = \"\";" +
		"				}" +
		"				tagCSSTexts[tagName] += cssText;" +
		"				if (!classCSSTexts[className]) {" +
		"					classCSSTexts[className] = \"\";" +
		"				}" +
		"				classCSSTexts[className] += cssText;" +
		"			} else {" +
		"				var tagName = name.toUpperCase();" +
		"				if (!tagCSSTexts[tagName]) {" +
		"					tagCSSTexts[tagName] = \"\";" +
		"				}" +
		"				tagCSSTexts[tagName] += cssText;" +
		"			}" +
		"		}" +
		"	}" +
		"	applyCSSTexts(htmlElement, tagCSSTexts, classCSSTexts);" +
		"}" +
		"function init(cssContent, lineSpacing, verticalAlign, wrap) {" +
		"	var htmlElement = document.body;" +
		"	htmlElement.style.overflow = \"hidden\";" +
		"	htmlElement.style.position = \"absolute\";" +
		"	parseCSS(htmlElement, cssContent);" +
		"	applyLineSpacing(htmlElement, lineSpacing);" +
		"	removeUnderLineForLinks(htmlElement);" +
		"	if (verticalAlign == 'CENTER') {" +
		"		htmlElement.style.verticalAlign = 'middle';" +
		"	} else if (verticalAlign == 'BOTTOM') {" +
		"		htmlElement.style.verticalAlign = 'bottom';" +
		"	} else {" +
		"		htmlElement.style.verticalAlign = 'left';" +
		"	}" +
		"	if (wrap != 'WRAP') {" +
		"		htmlElement.style['whiteSpace'] = 'nowrap';" +
		"	} else {" +
		"		htmlElement.style['wordWrap'] = 'break-word';" +
		"		htmlElement.style['whiteSpace'] = 'normal';" +
		"	}" +
		"	htmlElement.style.top = - lineSpacing * 0.85 + 'px';" +
		"	htmlElement.style.left = '0px'" +
		"}" +
		"</script></head>" +
		"<body onload=\'init(" +
			"\"" + css + "\"," + htmlText.getLeading() + ",\"" + options.getVerticalAlign() + "\",\"" + options.getTextWrap() + "\")\'>" +
			htmlText.getHtmlText() + 
		"</body></html>");

        String temp = html.toString();
        
        // load data
        webView.loadDataWithBaseURL(null, temp, "text/html", "utf-8", null);

        LogUtil.info(getLogger(), this, methodName, "End loading HTML text: {0}", temp);
    }

    /**
     * Gets {@link XREHTMLText}.
     *
     * @return the corresponding {@link XREHTMLText}
     */
    private XREHTMLText getHTMLText() {
        return (XREHTMLText) getXREResource();
    }
}
